#ifndef __FORMAT__
#define __FORMAT__
#include <string>
#include <memory>
#include <vector>
#include <sstream>
#include <assert.h>
#include "LogMsg.hpp"

/*
    Format.hpp：
        解析格式字符串_pattern,边解析变从LogMsg中获取格式子项(FormatItem)，存储到vector内部
        解析完成后，从vector中获取格式子项进行字符串的序列化
    FormatItem是一个基类，因为存在很多的 格式子项,这些格式子项都是派生类。 使用了多态


    1.理清思路
    2.分模块
    3.填充模块，写函数接口
    4.完成函数
*/

/*
    自定义输出格式
    [%d{%H:%M:%S}] %m%n
    类似于 %d --> 输出整形这样的
    输出格式 - 含义
    %d      - 日期 存在子格式  %H:%M:%S
    %T      - 水平制表符
    %p      - 日志等级
    %t      - 线程id
    %f      - 错误文件名
    %l      - 错误行号
    %m      - 日志消息 - 具体的错误信息
    %n      - 换行
    %N      - 日志器名字
    其他字符
*/

namespace Log
{
    using std::string;
    // 获取格式化子项
    // 根据%d，能获取到日期
    // 根据%p, 能获取到日志等级

    // 根据一个基类，派生出不能的子类，来获取不同的格式子项
    // 存在不同的格式化子项，我们可以使用继承来完成对不同格式化子项的获取
    // 未来通过基类指针去获取不同的格式化子项
    class FormatItem
    {
    public:
        // 对于多态&继承关系,一定使用基类的指针去访问派生类
        // 所以需要对基类指针进行一个重命名
        using ptr = std::shared_ptr<FormatItem>;

        ~FormatItem()
        {}
        // 从日志属性中，根据 输出格式，获取到对应格式的内容
        // 根据%d，能获取到日期

        // 为什么使用ostream？
        // ostream是stringstream的基类, 也是ostringstream的基类
        // 字符串的格式化控制 <---> stringstream(istringstream,ostringstream)
        virtual void format(std::ostream &os, const LogMsg &msg) = 0;

    };
    // 在派生类中为什么都存在私有成员 string ？ 有些可以没有 string，但有些必须有 string
    // 为了接口方便使用,统一

    // %d -- 日期 存在格式子项 %H:%S:%M
    class DateFormatItem : public FormatItem
    {
    private:
        // 添加私有变量表示时间子项
        string _subD;
    public:
        DateFormatItem(string subD = "%H:%S:%M")
        :_subD(subD)
        {}

        void format(std::ostream &os, const LogMsg &msg)
        {
            time_t time = msg._stamp;
            // localtime: 将时间戳转化为对应的结构体
            struct tm* t = localtime(&time);
            // 将对转换的结构体，根据时间子项，再进行转换
            // 使用 strftime, %H,%S,%M 就是strftime内部定义的时间子项
            char buf[64];
            strftime(buf,sizeof(buf),_subD.c_str(),t);
            os << buf;
        }
    };
    //%t -- 水平制表符
    class TableFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        TableFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg)
        {
            os << "\t";
        }

    };
    // %p -- 日志等级
    class LevelFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        LevelFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << LogLevel::toString(msg._level);
        }

    };
    // %t -- 线程id
    class TidFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        TidFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._tid;
        }

    };
    // %f -- 文件名
    class FileFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        FileFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << msg._file;
        }

    };
    // %l -- 行号
    class LineFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        LineFormatItem(string str = "")
        :_str(str)
        {}
        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << msg._line;
        }

    };
    // %m -- 错误信息
    class InfoErrFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        InfoErrFormatItem(string str = "")
        :_str(str)
        {}
        void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._msg;
        }

    };
    // %n -- 换行
    class EnterFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        EnterFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << "\n";
        }
    };
    
    class LoggerNameFormatItem : public FormatItem
    {
    private:    
        string _str;
    public:
        LoggerNameFormatItem(string str = "")
        :_str(str)
        {}

        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << msg._logger;
        }
    };


    class OtherFormatItem : public FormatItem
    {
    private:    
        // 添加成员变量来表示其他字符：如[,],a,v,g
        string _str;
    public:
        OtherFormatItem(string str = "")
        :_str(str)
        {}
        void format(std::ostream &os, const LogMsg &msg) 
        {
            os << _str;
        }
    };

    // 格式转换
    // 将  [%d{%H:%M:%S}] %m%n 格式化转换
    // --> [10:38:25] socket is closed\n
    // 格式转换器
    class Formatter
    {
    public:
        using ptr = std::shared_ptr<Formatter>;
        // %d的子子格式是通过strftime来的 
        Formatter(const string& pattern = "[%d{%H:%M:%S}] %m%n")
        :_pattern(pattern)
        {
            bool ret = AnalysePattern();
            assert(ret);
        }

        void ChangePattern(const string& patter)
        {
            _pattern = patter;
            _items.clear();
        }

        //  解析_pattern,具体的内容是从msg中获取的
        string format(const LogMsg& msg)
        {
            std::stringstream ss;
            string debug;
            for(auto& ele : _items)
            {
                ele->format(ss,msg);
                debug = ss.str();
            }
            return ss.str();
        }

        // 是一个重载函数，某个场景下使用
        // 在落地模块中，不同的落地类，使用这个函数接口
        void format(std::ostream& os, const LogMsg& msg)
        {
            for(auto& ele : _items)
            {
                ele->format(os,msg);
            }

        }

        // d -- DateFormatItem
        // ....
        // %d{%H:%M:%S}
        // 单独%d,是子项,{}内部是子子项

        // 自定义输出格式
        // [%d{%H:%M:%S}] %m%n
        // 类似于 %d --> 输出整形这样的
        // 输出格式 - 含义
        // %d      - 日期 存在子格式  %H:%M:%S
        // %T      - 水平制表符
        // %p      - 日志等级
        // %t      - 线程id
        // %f      - 错误文件名
        // %l      - 错误行号
        // %m      - 日志消息 - 具体的错误信息
        // %n      - 换行
        // %N      - 日志器名字
        // 其他字符

        // 根据特定的字符串"d","T"... ,创建格式子项-->创建一个派生类的智能指针，返回基类指针
        FormatItem::ptr CreateFormatlterm(const string& str,const string& substr)
        {
            // make_shared<Type>(Init)
            // 也可以 return  FormatItem::ptr(new DateFormatItem(substr))
            if(str == "d")  return std::make_shared<DateFormatItem>(substr);
            if(str == "T")  return std::make_shared<TableFormatItem>(substr);
            if(str == "p")  return std::make_shared<LevelFormatItem>(substr);
            if(str == "t")  return std::make_shared<TidFormatItem>(substr);
            if(str == "f")  return std::make_shared<FileFormatItem>(substr);
            if(str == "l")  return std::make_shared<LineFormatItem>(substr);
            if(str == "m")  return std::make_shared<InfoErrFormatItem>(substr);
            if(str == "n")  return std::make_shared<EnterFormatItem>(substr);
            if(str == "N")  return std::make_shared<LoggerNameFormatItem>(substr);
            // 其他
            return std::make_shared<OtherFormatItem>(str.substr(0,1));
        
        }

        bool AnalysePattern()
        {
            // std::cout << "_pattern：" << _pattern << std::endl;
            // 解析格式化子项 [%d{%H:%M:%S}] %m%n，获取对应的FormaltItem
            int i = 0;
            int n = _pattern.size();
            while(i < n)
            {
                if(_pattern[i] == '%')
                {
                    // 跳过%
                    i++;
                    if(i < n)
                    {
                        // "%%" 打印%
                        // 要考虑单个字符%，想打印%
                        if(_pattern[i] == '%')
                        {
                            i++;
                            _items.push_back(CreateFormatlterm("%",""));
                        }
                        else
                        {
                            // 这里也会把非设置的格式控制的情况考虑
                            // 比如说%a,%b,这种无效格式控制，在CreateFormatItem中做了排除
                            // 即使存在 %a{%H:%S:%M} 也只会当作a字符
                            
                            // 有效字符,存在格式控制的字符 
                            string vaildChar = "dTptflmnN";
                            bool vaild = true;
                            auto pos = vaildChar.find(_pattern[i]);
                            if(pos == string::npos)
                            {
                                vaild = false;
                            }

                            // 编译器存在一些问题to_string('a')，会使用其int
                            // [%d{%H:%M:%S}] [%p] [%f:%l] %m%n
                            string str;
                            str += _pattern[i++];

                            string substr;
                            // 说明存在 子子项
                            if(i < n && _pattern[i] == '{')
                            {
                                // 跳过{
                                i++;
                                while(i < n && _pattern[i] != '}')
                                {
                                    substr += _pattern[i++];
                                }
                                if(i == n)
                                {
                                    std::cout << "无效{}" << std::endl;
                                    return false;
                                }
                                // 跳过}
                                i++;
                            }
                            // 存在子子项 & 不存在子子项 
                            if(vaild)
                                _items.push_back(CreateFormatlterm(str,substr));
                            else
                                std::cout << "没有对应的%" << str << "控制" << std::endl;

                        }
                    }
                }
                else
                {
                    // 只有%后面明确的字符,加上{},才存在子子项，其他情况都算不存在
                    // 即使有{}也会算
                    string str;
                    str +=_pattern[i++];
                    // 用来过滤 dTptflmn这些特殊字符的匹配
                    str += "-";
                    _items.push_back(CreateFormatlterm(str,""));
                }

            }// end_while

            return true;
        }


    private:
        // 例如Log("[%d{%H:%M:%S}] %m%n",...);
        string _pattern; // 需要解析的格式
        // 存放基类指针
        // 通过基类指针来获取不同的
        std::vector<FormatItem::ptr> _items; // 格式化子项数组
        // 根据 "[%d{%H:%M:%S}] %m%n",获取对应格式的FarmatItem派生类

    };
}

#endif